/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package giuliani_mapelli_marocco.business;

import giuliani_mapelli_marocco.entity.Event;
import giuliani_mapelli_marocco.entity.User;
import giuliani_mapelli_marocco.entity.Weatherforecast;
import giuliani_mapelli_marocco.entity.Weathernotification;
import giuliani_mapelli_marocco.enumerations.WeatherMessageType;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.ejb.EJBException;
import javax.ejb.Stateless;
import javax.mail.MessagingException;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;

@Stateless
public class WeatherNotificationManager {
    
    @PersistenceContext
    private EntityManager em;
     
    @EJB
    private WeatherManager wm;
    
    @EJB
    private EmailManager emailm;
     /**
      * This method identifies if the weather is bad or not for a single day.
      * @param weather the weather forecast to analyze
      * @return true if is bad weather false otherwise
      */
     private boolean isWeatherBad(Weatherforecast weather) {
        
         int code = weather.getWeather();
         
         switch(code/100) {
             
             case 8:
                 return false;
             
             case 9:
                 if(code>=951 && code<=956) {
                     return false;
                 } else {
                     return true;
                 }
         }
        return true;
    }
     
     /**
      * This method returns the overall weather condition for an event.
      * @param event the event for which we have to test the weather conditions
      * @return true if the number of bad days are more or equals to the number of good days, false othwerwise
      */
     public boolean isEventBadMultiDay(Event event) { 
         
        List<Weatherforecast> forecast = wm.getWeatherByIdEvent(event.getId());
        int numberOfbadDays = 0;
        int numberOfgoodDays = 0;
        
        for(Weatherforecast f: forecast) {
             if(isWeatherBad(f)) {
                 numberOfbadDays++;
             } else {
                 numberOfgoodDays++;
             }
         }
         return numberOfbadDays > numberOfgoodDays;
     }
     
     /**
      * This method creates the Notification for the weather changed regarding one specific event and one specific user, checking
      * if a similar notification is already inside the DB (in this case is not recreated but is setted to unseen).
      * @param event event in which some weather notification are changed
      * @param kindOfNotification kind of notification we are considering 
      * @throws EJBException 
      */
     public void createNotificationWeather(Event event, WeatherMessageType kindOfNotification) {
            
         List<User> users = (List<User>)event.getUserCollection1();
         //If is a notification to a organizer I get only him from participants
         if(kindOfNotification.equals(WeatherMessageType.ORGANIZER_NOTIFICATION)) {
             List<User> temp = new ArrayList();
             for(User u1: users) {
                 if((event.getOrganizer().getUsername()).equals(u1.getUsername())) {
                  temp.add(u1);
                  break;
                 }
             }
             users = temp;
         }
         for(User u: users) {
             try{
             Weathernotification notification = checkIfAlreadyExistentNotification(event, u, kindOfNotification);
             //If a similar notification is already present for the same event it became unseen again and stored in the DB
             if(notification.getSeen()) {
                notification.setSeen(false);
                em.merge(notification);
                em.flush();
             }
          
             } catch(NoResultException ex) {
                //If it catches it means it wasn't find in the DB so we have to create it
            Weathernotification weatherNotification = new Weathernotification();
            weatherNotification.setIdEvent(event);
            weatherNotification.setType(kindOfNotification);
            weatherNotification.setAddressee(u);
            weatherNotification.setSeen(false);
            Date suggestedDate = new Date();
            if(kindOfNotification.equals(WeatherMessageType.ORGANIZER_NOTIFICATION)) {
                try {
                    Date date = wm.calculateTheClosestSunnyDay(event);
                    suggestedDate = date;
                    if(date!=null)
                    weatherNotification.setSuggestedDate(date);   
                } catch (IOException ex1) {
 
                }
            }
            if(kindOfNotification.equals(WeatherMessageType.PARTICIPANT_NOTIFICATION)) {
                try {
                    emailm.sendDayBeforeBadWeatherEmail(event, u);
                } catch (MessagingException ex1) {
                    Logger.getLogger(WeatherNotificationManager.class.getName()).log(Level.SEVERE, null, ex1);
                }
            } else if (kindOfNotification.equals(WeatherMessageType.ORGANIZER_NOTIFICATION)) {
                try {
                    emailm.SendThreeDaysBadWeatherEmail(event, u, suggestedDate);
                } catch (MessagingException ex1) {
                    Logger.getLogger(WeatherNotificationManager.class.getName()).log(Level.SEVERE, null, ex1);
                }
            }
            em.persist(weatherNotification);
             }
         }       
     }
     
     /**
      * This method will retrieve a particular Weathernotification istance (if any) in the DB.
      * @param idEvent id associated to the notification
      * @param user the user associated with the notification
      * @param kindOfNotification the enum type of the Weathernotification
      * @return the istance of that particular type of notification (if it is present)
      * @throws EJBException throws if the istance is not present in the DB
      */
     private Weathernotification  checkIfAlreadyExistentNotification(Event event, User user,WeatherMessageType kindOfNotification ) {
         
         
            Weathernotification notification = (Weathernotification) em.createQuery("SELECT n FROM Weathernotification n"
                    + " WHERE (n.type =:type AND n.addressee =:addressee AND n.idEvent =:idEvent)")
                    .setParameter("type", kindOfNotification).setParameter("addressee", user).setParameter("idEvent", event).getSingleResult();
      
            return notification;        
             }
     
     /**
      * This method will return all the weathernotification of the user
      * @param u User of wich we want to have the weathernotifications 
      * @return the List of Weathernotification of the user
      */
     public List<Weathernotification> getAllUserWeatherNotifications(User u) {
         
         List<Weathernotification> notification = (List<Weathernotification>) em.createQuery("SELECT w FROM Weathernotification w WHERE w.addressee =:addressee")
                 .setParameter("addressee", u).getResultList();
         return notification;
     }
     
     /**
      * This method will delete the notification using its id
      * @param id id of the weathernotification to delete
      */
     public void deleteWeatherNotification(Long id) {
         
         Weathernotification notification = (Weathernotification )em.createNamedQuery("Weathernotification.findById").setParameter("id", id).getSingleResult();
         em.remove(notification);
         em.flush();
         
     }
}
